using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using System.Timers;

namespace TrafficLightSim
{
    /// <summary>
    /// This is the main type for your game
    /// </summary>
    public class Game1 : Microsoft.Xna.Framework.Game
    {
        #region Field Region
        GraphicsDeviceManager graphics;
        public SpriteBatch spriteBatch;
        public static Game1 GameRef;

        public static bool simOn; //indicate if the simulation is on/not
        public static int simChange; //to ensure that the data couldn't be changed when sim is running

        const int screenWidth = 760;
        const int screenHeight = 700;
        const int simHeight = 560;

        Simulation sim;
        Menu menu;
        SpriteFont font;
        Texture2D hCarLeft, hCarRight, vCarUp, vCarDown, hlane, vlane;
        Texture2D red, yellow, green;
        Vector2 origin;
        double time;
        System.Timers.Timer timer;

        #endregion

        #region Property Region

        public int SimHeight
        {
            get { return simHeight; }
        }

        public int ScreenWidth
        {
            get { return screenWidth; }
        }

        public int ScreenHeight
        {
            get { return screenHeight; }
        }

        public Simulation Sim
        {
            get { return sim; }
        }

        public double Time
        {
            get { return time; }
            set { time = value; }
        }

        #endregion

        public Game1()
        {
            graphics = new GraphicsDeviceManager(this);
            graphics.PreferredBackBufferHeight = screenHeight; //set the backbuffer height to screenHeight
            graphics.PreferredBackBufferWidth = screenWidth; //set backbuffer height to screenWidth;

            Content.RootDirectory = "Content";
            GameRef = (Game1)this;
        }

        /// <summary>
        /// Allows the game to perform any initialization it needs to before starting to run.
        /// This is where it can query for any required services and load any non-graphic
        /// related content.  Calling base.Initialize will enumerate through any components
        /// and initialize them as well.
        /// </summary>
        protected override void Initialize()
        {
            // TODO: Add your initialization logic here
            this.IsMouseVisible = true;
            sim = new Simulation();
            base.Initialize();

            menu.Initialize();
            sim.Initialize();


            //initialize timer, which will change the traffic light every 0.5s
            timer = new System.Timers.Timer(2000);

            timer.Elapsed += new ElapsedEventHandler(OnTimedEvent);
        }



        /// <summary>
        /// LoadContent will be called once per game and is the place to load
        /// all of your content.
        /// </summary>
        protected override void LoadContent()
        {
            // Create a new SpriteBatch, which can be used to draw textures.
            spriteBatch = new SpriteBatch(GraphicsDevice);

            // TODO: use this.Content to load your game content here
            font = Content.Load<SpriteFont>(@"MainFont");
            menu = new Menu(font);
            red = Content.Load<Texture2D>(@"Sprites\red");
            yellow = Content.Load<Texture2D>(@"Sprites\yellow");
            green = Content.Load<Texture2D>(@"Sprites\green");
            vCarUp = Content.Load<Texture2D>(@"Sprites\carUp");
            vCarDown = Content.Load<Texture2D>(@"Sprites\carDown");
            hCarLeft = Content.Load<Texture2D>(@"Sprites\carLeft");
            hCarRight = Content.Load<Texture2D>(@"Sprites\carRight");
            hlane = Content.Load<Texture2D>(@"Sprites\hLane");
            vlane = Content.Load<Texture2D>(@"Sprites\vLane");
        }

        /// <summary>
        /// UnloadContent will be called once per game and is the place to unload
        /// all content.
        /// </summary>
        protected override void UnloadContent()
        {
            // TODO: Unload any non ContentManager content here
        }

        /// <summary>
        /// Allows the game to run logic such as updating the world,
        /// checking for collisions, gathering input, and playing audio.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Update(GameTime gameTime)
        {

            // TODO: Add your update logic here
            menu.Update();
            sim.UpdateLanes();
            if (simOn) //if simulation is running
            {
                timer.Enabled = true;
                time += gameTime.ElapsedGameTime.TotalMilliseconds; //get the time since the latest update call and add it up

                if (simChange == 1)
                {
                    UpdateSimData(); // update the data of the simulation
                    simChange = 0;
                }
                sim.UpdateSimulation(time);

            }

            if (time >= sim.SimRun * 5000 || simOn == false) //one sim = 5s, if more than that, stop sim
            {
                simOn = false;
                timer.Enabled = false;
                simChange = 0;
                time = 0; //reset the time for simulation purpose
            }

            base.Update(gameTime);
        }

        /// <summary>
        /// This is called when the game should draw itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        protected override void Draw(GameTime gameTime)
        {
            GraphicsDevice.Clear(Color.ForestGreen);

            // TODO: Add your drawing code here
            spriteBatch.Begin();

            #region Lane Drawing

            foreach (Lane l in sim.HLanes)
                spriteBatch.Draw(hlane,
                    l.LaneRect,
                    Color.White);
            foreach (Lane l in sim.VLanes)
                spriteBatch.Draw(vlane,
                    l.LaneRect,
                    Color.White);

            #endregion

            #region Traffic Light Drawing
            if (sim.TrafficLight[0].State == LightState.Red) //traffic light drawing for the direction down
                spriteBatch.Draw(red,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    180 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);
            else if (sim.TrafficLight[0].State == LightState.Yellow)
                spriteBatch.Draw(yellow,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    180 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);
            else if (sim.TrafficLight[0].State == LightState.Green)
                spriteBatch.Draw(green,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    180 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);

            #region Traffic light right region
            if (sim.TrafficLight[1].State == LightState.Red) //traffic light drawing for the direction down
                spriteBatch.Draw(red,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    90 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);
            else if (sim.TrafficLight[1].State == LightState.Yellow)
                spriteBatch.Draw(yellow,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    90 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);
            else if (sim.TrafficLight[1].State == LightState.Green)
                spriteBatch.Draw(green,
                    new Rectangle(sim.VLanes.Last().LaneRect.Right, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                    new Rectangle(0, 0, green.Width, green.Height),
                    Color.White,
                    90 * MathHelper.Pi / 180,
                    new Vector2(green.Width, green.Height),
                    SpriteEffects.None,
                    0f);
            #endregion

            if (sim.IsTwoWay)
            {
                #region Left direction traffic light

                if (sim.TrafficLight[3].State == LightState.Red) //traffic light drawing for the direction down
                    spriteBatch.Draw(red,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        -90 * MathHelper.Pi / 180,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);
                else if (sim.TrafficLight[3].State == LightState.Yellow)
                    spriteBatch.Draw(yellow,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        -90 * MathHelper.Pi / 180,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);
                else if (sim.TrafficLight[3].State == LightState.Green)
                    spriteBatch.Draw(green,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.Last().LaneRect.Bottom, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        -90 * MathHelper.Pi / 180,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);

                #endregion

                #region Up Direction Traffic Light
                if (sim.TrafficLight[2].State == LightState.Red) //traffic light drawing for the direction down
                    spriteBatch.Draw(red,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        0,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);
                else if (sim.TrafficLight[2].State == LightState.Yellow)
                    spriteBatch.Draw(yellow,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        0,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);
                else if (sim.TrafficLight[2].State == LightState.Green)
                    spriteBatch.Draw(green,
                        new Rectangle(sim.VLanes.First().LaneRect.Left, sim.HLanes.First().LaneRect.Top, green.Width, green.Height),
                        new Rectangle(0, 0, green.Width, green.Height),
                        Color.White,
                        0,
                        new Vector2(green.Width, green.Height),
                        SpriteEffects.None,
                        0f);

                #endregion
                }
            #endregion

            menu.Draw();

            #region Car Drawing
            foreach (Car c in sim.Cars) //draw all the cars in the Cars list
            {
                if (c.Direction == Direction.Down)
                    spriteBatch.Draw(vCarDown,
                        new Rectangle((int)c.Position.X, (int)c.Position.Y, 35, 50),
                        Color.White);
                else if (c.Direction == Direction.Up)
                    spriteBatch.Draw(vCarUp,
                        new Rectangle((int)c.Position.X, (int)c.Position.Y, 35, 50),
                        Color.White);
                else if (c.Direction == Direction.Right)
                    spriteBatch.Draw(hCarRight,
                        new Rectangle((int)c.Position.X, (int)c.Position.Y, 50, 35),
                        Color.White);
                else if (c.Direction == Direction.Left)
                    spriteBatch.Draw(hCarLeft,
                        new Rectangle((int)c.Position.X, (int)c.Position.Y, 50, 35),
                        Color.White);
            }


            #endregion

            spriteBatch.End();
            base.Draw(gameTime);
        }

        #region Non XNA Method

        //update simulation data, update the variables stored in Simulation class
        public void UpdateSimData()
        {
            sim.FlushSimData();
            sim.SimRun = Convert.ToInt32(menu.SimRunContent);
            sim.HProb = Convert.ToDouble(menu.HProbContent);
            sim.VProb = Convert.ToDouble(menu.VProbContent);
            sim.HLane = Convert.ToInt32(menu.HLaneContent);
            sim.VLane = Convert.ToInt32(menu.VLaneContent);
            if (menu.ModeContent == "Normal")
                sim.IsTwoWay = false;
            else sim.IsTwoWay = true;
        }

        //the method that is called every 0.5s, should call the method that'll update traffic light
        private void OnTimedEvent(object source, ElapsedEventArgs e)
        {
            sim.ToggleTrafficLight();
        }

        public void ToggleTwoWay()
        {
            if (sim.IsTwoWay)
                sim.IsTwoWay = false;
            else if (!sim.IsTwoWay)
                sim.IsTwoWay = true;
        }

        #endregion
    }
}
